package tiltmaze.solver;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import tiltmaze.Cell;
import tiltmaze.Maze;
import tiltmaze.RowCol;

/**
 * Class representing the motion graph of a maze.
 * @author Juraj Nižnan
 */
public class MazeMotionGraph {

    HashSet<Vertex> vertices;
    Vertex start;

    /**
     * Constructor.
     * @param maze
     */
    public MazeMotionGraph(Maze maze) {
        int num = 0;
        HashMap<RowCol, Integer> checkpoints = new HashMap<RowCol, Integer>();
        Cell[][] cells = maze.getCells();
        for (int i = 0; i < maze.getNumOfRows(); i++) {
            for (int j = 0; j < maze.getNumOfCols(); j++) {
                if (cells[i][j].getCheckpoint()) {
                    checkpoints.put(new RowCol(i, j), num);
                    num++;
                }
            }
        }
        vertices = new HashSet<Vertex>();
        HashSet<Vertex> unresolvedVertices = new HashSet<Vertex>();
        start = new Vertex(maze.getStart());
        vertices.add(start);
        unresolvedVertices.add(start);
        while (!unresolvedVertices.isEmpty()) {
            for (Vertex v : new HashSet<Vertex>(unresolvedVertices)) {
                unresolvedVertices.remove(v);
                for (int i = 0; i < 4; i++) {
                    int col1 = v.position.col;
                    int row1 = v.position.row;
                    while (!maze.getCells()[row1][col1].getWalls()[i]) {
                        switch (i) {
                            case 0:
                                row1--;
                                break;
                            case 1:
                                col1++;
                                break;
                            case 2:
                                row1++;
                                break;
                            case 3:
                                col1--;
                                break;
                        }
                        RowCol rc = new RowCol(row1, col1);
                        if (checkpoints.containsKey(rc)) {
                            v.edges[i] = v.edges[i] | MazeSolver.exp(checkpoints.get(rc));
                        }
                    }
                    RowCol rc = new RowCol(row1, col1);
                    if (!rc.equals(v.position)) {
                        Vertex newV = new Vertex(rc);
                        Vertex targetV = null;
                        for (Vertex v1 : vertices) {
                            if (v1.equals(newV)) {
                                targetV = v1;
                            }
                        }
                        if (targetV == null) {
                            targetV = newV;
                            unresolvedVertices.add(newV);
                            vertices.add(newV);
                        }
                        v.targets[i] = targetV;
                    }
                }
            }
        }
    }

    public void writeGML(String filename) throws IOException {
        BufferedWriter out = new BufferedWriter(new FileWriter(filename));
        out.write("graph\n");
        out.write("[\n");
        out.write("hierarchic 1\n");
        out.write("label \"\"\n");
        out.write("directed 1\n");
        for (Vertex v : vertices) {
            out.write("node\n[id " + v.position.hashCode() + " label \"" + v.position + "\"]\n");
        }
        for (Vertex v : vertices) {
            for (int i = 0; i < 4; i++) {
                if (v.targets[i] != null) {
                    out.write("edge\n[source " + v.position.hashCode() + " target " + v.targets[i].position.hashCode() + " label \"" + i + ":" + v.edges[i] + "\"]\n");
                }
            }
        }
        out.write("]");
        out.close();
    }



}
